工具&方法丨经生小白会敲代码,还会写爬虫防坑指南
引言
(不好意思,走错片场了)
四五天没问题吧?
没问题!
准备工作
pip install selenium
下载链接 https://sites.google.com/a/chromium.org/chromedriver/downloads
from selenium import webdriver
engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe' # 插件的地址
options = webdriver.ChromeOptions()
options.add_argument("--headless") # 不加这条试试这条会发生什么
# 低配版请求参数就算搞定了,可以用这个driver虚拟浏览器访问啦,复杂的还可加上请求头设置,ip设置等
driver = webdriver.Chrome(executable_path=engine_path, options=options)
driver.get('https://www.baidu.com/') # 访问网址
driver.find_element_by_id("kw").send_keys("python") # 给id为"kw"的元素即搜索框输入信息"python"
driver.find_element_by_id("su").click() # 点击百度一下
左右滑动查看更多
除了get()之外,以下列举了一些对网页的操作:
driver.current_url:用于获得当前页面的URL。
driver.title:用于获取当前页面的标题。
driver.page_source:用于获取页面html源代码。
driver.forward():浏览器向前(点击向前按钮)。
driver.back():浏览器向后(点击向后按钮)。
driver.refresh():浏览器刷新(点击刷新按钮)。
driver.close():关闭当前窗口,或最后打开的窗口。
driver.quit():关闭所有窗口。
driver.switch_to_window(窗口句柄):切换到新窗口。
打开网页后,我们需要找到网页内的目标元素。一般找元素的方法为在谷歌浏览器按F12打开开发者工具,用开发者工具左上角的小鼠标找到你需要的元素并复制xpath语句到程序中(具体方法可以参考Wallace同学的文章),以下列举了一些定位元素的方法:
find_element_by_id() # id定位
find_element_by_name() # name定位
find_element_by_class_name() # class定位
find_element_by_xpath() # xpath定位
find_element_by_css_selector() # css定位
左右滑动查看更多
element.text:获取元素的文本。
element.tag_name:获取标签名称。
element.clear():清除文本。
element.send_keys(value):输入文字或键盘按键。
element.click():单击元素。
element.get_attribute(name):获得属性值
//*[@id="1"]/h3/a
//*[@id="2"]/h3/a
//*[@id="3"]/h3/a
...
//*[@id="61"]/h3/a
//*[@id="62"]/h3/a
...
踩坑出坑
import selenium.webdriver as webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe' # 插件安装的地址
options = webdriver.ChromeOptions() # 可以在这里给谷歌浏览器修改参数
# 低配版请求参数就算搞定了,可以用这个driver虚拟浏览器访问啦,复杂的还可加上请求头设置,ip设置等
driver = webdriver.Chrome(executable_path=engine_path, options=options)
url = 'https://www.baidu.com/' # 在引号内输入网址
driver.get(url) # 开始请求访问
# 找到id为‘kw’(即为搜索框)并在搜索框中输入python
driver.find_element_by_id("kw").send_keys("python")
driver.find_element_by_id("su").click() # 点击搜索
driver.find_element_by_xpath('//*[@id="page"]/a[10]').click() # 在搜索结果中点击下一页
driver.find_element_by_xpath('//*[@id="page"]/a[10]').click()
左右滑动查看更多
在经过一系列自闭操作后,我终于明白这是因为代码执行速度很快,但浏览器响应很慢,页面的元素还没有加载完,找不到该元素。在小刘老师的建议下,将原来的错误语句之前加上成这三条:
page_xpath = '//*[@id="page"]/a[10]'
bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)
# 在20s内等待找到这一元素为止,超出则报错
webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)
左右滑动查看更多
1)元素标识使用不正确;
2)素可能在frame标签下。如果是需要写切换到frame中在进行查找(没遇到过);
3)元素属性可能是隐藏的(没遇到过);
4)一般的爬虫程序都是处于重复的自动运行状态,一旦一个网页链接失败,找不到元素,就可能导致下一步程序无法也无法找到相应元素,这时便需要多加一些try语句来处理异常。
1)前面设定的bool_func定位标识输入错误,确实无该元素;
👉解决办法:检查前面输入的元素定位符的语句。2)页面失效,或者网页元素加载失败;👉解决办法:如果该页面失效,解决的方法就是使用try语句,当遇到网页无法链接的错误时,跳过该条网址,继续进行下一个。当然若是因为IP频繁访问,或者被检测到是爬虫程序而被拒绝访问,解决方法可以选择添加IP代理proxy()、cookies等请求元素。3)自动化运行的程序中出现某个错误,导致当前页面不是在理想的目标页面下,所以也就找不到该元素。👉解决办法:则需要好好看一下整个程序的运行流程,是否会因为前置的错误影响后续的进程,加—上纠错机制。以下为2)的例子:
import selenium.webdriver as webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe'
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(executable_path=engine_path, options=options)
url = 'https://www.baidu.com/'
driver.get(url)
driver.find_element_by_id("kw").send_keys("python")
driver.find_element_by_id("su").click()
page_xpath = '//*[@id="1"]/h3/a'
bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)
webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)
driver.find_element_by_xpath('//*[@id="1"]/h3/a').click() # 点击进入搜索结果的第一个网页
page_xpath = '//*[@id="submit"]' # 我在该网页内随便选的一个元素
bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)
webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)
print('完成该页面爬取')
print('第二页')
由于python的官方网站是一个国外的网址,国内虽然可以访问,但加载速度非常慢,常常会超出时间或直接无法加载。
修改:
try:
page_xpath = '//*[@id="submit"]'
bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)
webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)
except:
print('该页面无法加载,跳过进行下一个')
else:
print('完成该页面爬取')
print('第二页')
左右滑动查看更多
1)元素之间存在逻辑关系:比如你要选择地址时,“中国”选择完毕之后,才能选择“北京”。如果想直接一步到位,则会出现element not visible。这种错误并不是元素定位错误所引起的,而是不符合网页代码逻辑;
👉解决办法:注意逻辑顺序的选取,按照网站的要求激活一个再激活下一个。
2)元素定位错误:如果网页中存在你需要定位的多个元素。如果出现element not visible,有可能是你想要的定位和现实的定位出现了不同;👉解决办法:也就是定位更精确,即定位更有区分性。可以采用By.Id,By.xpath(expression)方式。3)元素定位到一个鼠标事件后才能进行事件触发的位置上:比如,需要鼠标移动到某个区域,元素才可以显示点击的按钮,如果鼠标离开,则相应的事件也没办法触发;👉解决办法:问题都已经描述清楚了,响应的解决办法也就有了,那就是引入action类,模拟鼠标移动到需要定位的元素下,让dom树从新生成,然后定位相应的元素。
例如我最近做的项目中,主要遇到了第一种的情况,如下图:
这是中华人民共和国海关里统计月报中板块,我想要爬取2018年美元值的第一张表。虽然这张表有它的xpath可以定位,网页源码中也确实存在该元素,但程序无法一步找到,需要像人操作一样先点“2018”,再点“美元”,才能找到该元素,并执行点击进入操作。
下面展示错误的代码:
import selenium.webdriver as webdriver
engine_path = r'C:\Users\Administrator\Desktop\chromedriver_win32.exe' # 需要修改为自己的chromedriver文件存储路径
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(executable_path=engine_path, options=options)
url = 'http://www.customs.gov.cn/customs/302249/302274/302277/index.html'
driver.get(url)
page_xpath = '//*[@id="ess_ContentPane"]/table[2]/tbody/tr[2]/td/div[1]/div/div/div[4]/table/tbody/tr[2]/td[2]/a[1]'
bool_func = lambda x: driver.find_elements_by_xpath(page_xpath)
webdriver.support.wait.WebDriverWait(driver, 20).until(bool_func)
driver.find_element_by_xpath('//*[@id="ess_ContentPane"]/table[2]/tbody/tr[2]/td/div[1]/div/div/div[4]/table/tbody/tr[2]/td[2]/a[1]').click()
左右滑动查看更多
Q
怎么修正这个错误?
A
公众号后台回复“防坑指南”,
查看正确答案。
一个总结
虽然说写代码的时候遇到报错是很伤脑筋的事情,有时候解决一个报错甚至要花掉大半个下午的时间(自信心大受打击),但是仔细回顾,解决代码错误也是另外一种提高的办法。问题就在那里,网页的实际情况就在那里。这一次爬虫没有遇到,总有会有遇到的时候,早遇早提高嘛!当然了,不单单是爬虫,写什么代码都会遇到报错,学会解决代码错误、总结各个异常情况的解决办法也是熟练度提升的标志。刚刚学习python的我还是要多练习多总结才是!
►往期推荐
回复【Python】👉 简单有用易上手
回复【学术前沿】👉机器学习丨大数据
回复【数据资源】👉公开数据
回复【可视化】👉 你心心念念的数据呈现
回复【老姚专栏】👉老姚趣谈值得一看
►一周热文
数据Seminar
这里是大数据、分析技术与学术研究的三叉路口
欢迎扫描👇二维码添加关注